home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 3 / Cream of the Crop 3.iso / sound / ptmid.zip / PTMIDSAV.C < prev    next >
C/C++ Source or Header  |  1994-01-08  |  11KB  |  344 lines

  1. /*
  2.  * ptmidsav.c: MOD-format saver module for ptmid. Takes a structure
  3.  * representing a tune and creates a file which contains that tune.
  4.  *
  5.  * Author: Andrew Scott  (c)opyright 1994
  6.  *
  7.  * Date: 17/11/1993 ver 0.0
  8.  *       8/1/1994   ver 0.1
  9.  */
  10.  
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include "ptmid.h"
  15.  
  16. /*
  17.  * PutblanksPfile: Outputs as many null characters to a given file as
  18.  * is desired (max. 131). Inelegant solution.
  19.  */
  20. void PutblanksPfile(FILE *pfile, int cch)
  21. {
  22.     if (cch)
  23.         fwrite("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
  24.             "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
  25.             "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
  26.             "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
  27.             "\0\0", 1, cch, pfile);
  28. }
  29.  
  30. /*
  31.  * PutPfileSz: Outputs a string to a given file and pads it to the correct
  32.  * length by outputting null characters if necessary.
  33.  */
  34. void PutPfileSz(FILE *pfile, Sz szT, int cch)
  35. {
  36.     while (*szT && cch) {
  37.         putc(*(szT++), pfile);
  38.         cch--;
  39.     }
  40.     PutblanksPfile(pfile, cch);
  41. }
  42.  
  43. /*
  44.  * WritePfile: Writes division information (sample, period, effect) to
  45.  * given file in the standard Protracker format.
  46.  */
  47. void WritePfile(FILE *pfile, unsigned bSam, unsigned wPer, unsigned wEff)
  48. {
  49.     putc((bSam & 0xF0) | (wPer >> 8), pfile);
  50.     putc(wPer & 0xFF, pfile);
  51.     putc((bSam & 0x0F) << 4 | (wEff >> 8), pfile);
  52.     putc(wEff & 0xFF, pfile);
  53. }
  54.  
  55. /*
  56.  * PeiNextPtune: Given a pointer to a tune, will start using it if not
  57.  * already using one. Will return next event in quanta, else NULL.
  58.  * If it gets to the end of the tune, will set flag to false and will
  59.  * wait to be given a new tune.
  60.  */
  61. EI *PeiNextPtune(Tune *ptuneMain, int *pf)
  62. {
  63.     static Tune *ptune = NULL;
  64.     static unsigned long quant;
  65.     EI *pei;
  66.  
  67.     if (NULL == ptune) { /** If first time called **/
  68.         if (NULL == ptuneMain) /** If no tune given, quit **/
  69.             return NULL;
  70.         *pf = 1;
  71.         quant = 0;
  72.         ptune = ptuneMain; /** Initialize tune pointer to start of tune **/
  73.     } else /** Else **/
  74.         quant++; /** Advance along tune 1 quantize fraction **/
  75.     if (quant < ptune->count) /** If haven't reached next event **/
  76.         return NULL; /** return nothing-happening **/
  77.  
  78.     pei = ptune->pei; /** Otherwise note next event **/
  79.     if ((ptune = ptune->ptune) == NULL) /** If no more events **/
  80.         *pf = 0; /** register end of tune **/
  81.     return pei; /** Return that event **/
  82. }
  83.  
  84. /*
  85.  * Convqpm: Converts a given qpm number into a double tempo thingy.
  86.  */
  87. void Convqpm(unsigned qpm, int rgbTempo[2], int ticks)
  88. {
  89.     if (792 / ticks <= qpm && 6144 / ticks - 1 >= qpm) {
  90.         rgbTempo[0] = ticks; /** If can use current ticks/div, do so **/
  91.         rgbTempo[1] = qpm * ticks / 24;
  92.     } else if (33 > qpm) /** Else if qpm is very small **/
  93.         if (26 > qpm) { /** approximate it **/
  94.             rgbTempo[0] = 31;
  95.             rgbTempo[1] = 33;
  96.         } else {
  97.             rgbTempo[0] = 31;
  98.             rgbTempo[1] = qpm * 31 / 24;
  99.         }
  100.     else if (6120 <= qpm) { /** Else if qpm is very big **/
  101.         rgbTempo[0] = 1; /** approximate it too **/
  102.         rgbTempo[1] = 255;
  103.     } else { /** Else look for closest fraction **/
  104.         unsigned iT;
  105.  
  106.         iT = (qpm + 254) / 255; /**### try best fit fraction later ###**/
  107.         rgbTempo[0] = 24 / iT;
  108.         rgbTempo[1] = qpm / iT;
  109.     }
  110. }
  111.  
  112. /*
  113.  * PutpatternsPtunePfile: Given an output file and a tune, will output the
  114.  * tune as standard Protracker patterns. wPtchan determines number of
  115.  * channels per division. wPatmax determines maximum number of patterns
  116.  * to write before terminating.
  117.  */
  118. int PutpatternsPtunePfile(Tune *ptune, FILE *pfile)
  119. {
  120.     int iT, iT2, wPat = 0, cDiv, ipw, ipwMax, fGoing, ini;
  121.     unsigned pwLen[MAXPTCHAN] = {0,0,0,0,0,0,0,0}, pwNote[3 * MAXPTCHAN];
  122.     unsigned rgbTempo[2] = {6,125};
  123.     long wNewtempo = 120;
  124.     EI *pei;
  125.     NI *pni;
  126.  
  127.     pei = PeiNextPtune(ptune, &fGoing); /** Get first event **/
  128.     ipw = wPtchan;
  129.     ipwMax = wPtchan * 3;
  130.     for (wPat = 0; fGoing; wPat++) { /** Loop until told to stop **/
  131.         if (wPat == wPatmax) { /** If pattern limit reached, stop **/
  132.             fprintf(stderr, "ptmid: Warning -- Pattern limit %d reached. "
  133.                 "Aborting!\n", wPatmax);
  134.             break;
  135.         }
  136.         for (cDiv = 64; cDiv--; ) { /** For each division in a pattern **/
  137.             memset(pwNote, 0, ipwMax * sizeof(unsigned)); /** Clear next notes **/
  138.  
  139.             for (iT = wPtchan; iT--; ) /** With any currently playing notes **/
  140.                 if (pwLen[iT])
  141.                     if (0 == --pwLen[iT]) { /** Check if just stopped **/
  142.                         pwNote[iT2 = iT * 3] = MAXPTSAMPS; /** Yes.. store quiet command **/
  143.                         pwNote[iT2 + 1] = 0;
  144.                         pwNote[iT2 + 2] = 0xC00;
  145.                     }
  146.  
  147.             if (fGoing) { /** If still going in the tune **/
  148.                 for (; NULL != pei; pei = pei->peiNext) /** For each event at this position **/
  149.                     if (pei->cni) { /** If note-event **/
  150.                         pni = pei->pni;
  151.                         for (ini = pei->cni; ini--; ) /** For each note **/
  152.                             if (-1 != pni[ini].inst) { /** which is valid **/
  153.  
  154.                                 iT = ipwMax - 1; /*** Find channel to stick note ***/
  155.                                 for (; 0 < iT && 0xC00 != pwNote[iT]; iT -= 3);
  156.                                 if (0 > iT) {
  157.                                     for (iT = ipw; iT--; )
  158.                                         if (0 == pwLen[iT])
  159.                                             break;
  160.                                     if (0 > iT) {
  161.                                         for (iT = wPtchan; iT-- > ipw; )
  162.                                             if (0 == pwLen[iT])
  163.                                                 break;
  164.                                         if (0 > iT) {
  165.                                             iT2 = pei->effect / 2;
  166.                                             for (iT = ipw; iT--; )
  167.                                                 if (iT2 <= pwLen[iT])
  168.                                                     break;
  169.                                             if (0 > iT) {
  170.                                                 for (iT = wPtchan; iT-- > ipw; )
  171.                                                     if (iT2 <= pwLen[iT])
  172.                                                         break;
  173.                                                 if (0 > iT)
  174.                                                     continue;
  175.                                             }
  176.                                         }
  177.                                     }
  178.                                     if (--ipw < 0)
  179.                                         ipw = wPtchan - 1;
  180.                                     iT = iT * 3 + 2;
  181.                                 }
  182.  
  183.                                 pwNote[iT - 2] = pni[ini].inst + 1; /*** Store note ***/
  184.                                 pwNote[iT - 1] = pni[ini].pitch;
  185.                                 if (pni[ini].vol != rgmsDecided[pni[ini].inst].bDefvol)
  186.                                     pwNote[iT] = 0xC00 + pni[ini].vol;
  187.                                 else
  188.                                     pwNote[iT] = 0;
  189.                                 pwLen[iT / 3] = pei->effect;
  190.                             }
  191.  
  192.                     } else /** Else store new tempo **/
  193.                         wNewtempo = pei->effect;
  194.                 pei = PeiNextPtune(NULL, &fGoing);
  195.             }
  196.  
  197.             if (0 != wNewtempo) { /** If need to set a tempo this division **/
  198.                 int rgbNew[2];
  199.  
  200.                 Convqpm(wNewtempo, rgbNew, rgbTempo[0]); /** Find protracker equivalent **/
  201.                 if (rgbNew[0] != rgbTempo[0] || rgbNew[1] != rgbTempo[1]) {
  202.                     for (iT = ipwMax - 1; iT > 0; iT -= 3) /** Find a channel for it **/
  203.                         if (0 == pwNote[iT])
  204.                             break;
  205.                     if (iT < 0) { /** If no channel.. damn. Have to replace something **/
  206.                         unsigned bMin = -1, bTest;
  207.  
  208.                         for (iT2 = ipwMax - 1; iT2 > 0; iT2 -= 3) {
  209.                             bTest = abs((pwNote[iT2] & 0xFF) - rgmsDecided[pwNote[iT2 - 2] - 1].bDefvol);
  210.                             if (bTest < bMin) {
  211.                                 bMin = bTest;
  212.                                 iT = iT2; /** Find best thing to replace **/
  213.                             }
  214.                         }
  215.                     }
  216.  
  217.                     if (rgbNew[0] != rgbTempo[0])
  218.                         if (rgbNew[1] != rgbTempo[1]) { /** If need two places **/
  219.                             pwNote[iT] = 0xFFF;
  220.                             for (iT2 = ipwMax - 1; iT2 > 0; iT2 -= 3) /** Find a channel **/
  221.                                 if (0 == pwNote[iT2])
  222.                                     break;
  223.                             if (iT2 < 0) { /** No channels.. use different new tempo **/
  224.                                 iT2 = rgbNew[1] * rgbTempo[0] / rgbNew[0];
  225.                                 if (255 < iT2)
  226.                                     if (280 < iT2)
  227.                                         iT2 = 0;
  228.                                     else
  229.                                         iT2 = 255;
  230.                                 else if (33 > iT2)
  231.                                     if (30 > iT2)
  232.                                         iT2 = 33;
  233.                                     else
  234.                                         iT2 = 0;
  235.                                 if (0 != iT2) { /** If we can allow for ~10% bpm variance **/
  236.                                     pwNote[iT] = 0xF00 + iT2; /** then use it **/
  237.                                     rgbTempo[1] = iT2;
  238.                                 } else { /** Else try to find a ticks value **/
  239.                                     iT2 = rgbNew[0] * rgbTempo[1] / rgbNew[1];
  240.                                     if (0 == iT2)
  241.                                         iT2 = 1;
  242.                                     else if (32 < iT2)
  243.                                         iT2 = 32;
  244.                                     pwNote[iT] = 0xF00 + iT2; /** and use it **/
  245.                                     rgbTempo[0] = iT2;
  246.                                 }
  247.                             } else {
  248.                                 pwNote[iT] = 0xF00 + rgbNew[0]; /** Store 2 value tempo **/
  249.                                 rgbTempo[0] = rgbNew[0];
  250.                                 pwNote[iT2] = 0xF00 + rgbNew[1];
  251.                                 rgbTempo[1] = rgbNew[1];
  252.                             }
  253.                         } else {
  254.                             pwNote[iT] = 0xF00 + rgbNew[0]; /** Store just ticks/div **/
  255.                             rgbTempo[0] = rgbNew[0];
  256.                         }
  257.                     else {
  258.                         pwNote[iT] = 0xF00 + rgbNew[1]; /** Store just bpm **/
  259.                         rgbTempo[1] = rgbNew[1];
  260.                     }
  261.                 }
  262.                 wNewtempo = 0;
  263.             }
  264.  
  265.             for (iT = ipwMax - 1; iT > 0; iT -= 3) /** Write division to file **/
  266.                 WritePfile(pfile, pwNote[iT - 2], pwNote[iT - 1], pwNote[iT]);
  267.         }
  268.     }
  269.     return wPat; /** Return number of patterns written **/
  270. }
  271.  
  272. /*
  273.  * SavePtunePfile: Given an output file and a tune, will output the tune to
  274.  * the file using standard Protracker MODule format.
  275.  */
  276. void SavePtunePfile(Tune *ptune, FILE *pfile)
  277. {
  278.     int iT, cPatterns, ch, cSamps = 0;
  279.     long Patternpos, Samplepos, cch;
  280.     SI **ppsi;
  281.     FILE *pfileSamp;
  282.     char *pch;
  283.  
  284.     PutPfileSz(pfile, szTitle, 20); /** Write title **/
  285.     for (iT = 0; iT < MAXPTSAMPS; iT++) { /** Write sample info **/
  286.         PutPfileSz(pfile, rgmsDecided[iT].szName, 22);
  287.         if (0 == rgmsDecided[iT].cMix)
  288.             PutblanksPfile(pfile, 8);
  289.         else {
  290.             cSamps++;
  291.             putw(0, pfile);
  292.             putc(0, pfile);
  293.             putc(rgmsDecided[iT].bDefvol, pfile);
  294.             ppsi = rgmsDecided[iT].ppsiMix;
  295.             putc((*ppsi)->wLppos >> 8, pfile);
  296.             putc((*ppsi)->wLppos & 0xFF, pfile);
  297.             putc((*ppsi)->wLplen >> 8, pfile);
  298.             putc((*ppsi)->wLplen & 0xFF, pfile);
  299.         }
  300.     }
  301.  
  302.     Patternpos = ftell(pfile); /** Store start of pattern # table **/
  303.     PutblanksPfile(pfile, 130); /** Write it as blanks **/
  304.     fwrite(szId, 4, 1, pfile); /** Write protracker id **/
  305.  
  306.     cPatterns = PutpatternsPtunePfile(ptune, pfile); /** Write patterns **/
  307.  
  308.     Samplepos = ftell(pfile); /** Store start of samples data **/
  309.     fseek(pfile, Patternpos, SEEK_SET); /** Go back to pattern # table **/
  310.  
  311.     putc(cPatterns, pfile);
  312.     putc(127, pfile);
  313.     for (iT = 0; iT < cPatterns; ) /** Write pattern numbers **/
  314.         putc(iT++, pfile);
  315.  
  316.     pch = (char *) malloc(512);
  317.     if (!fQuiet)
  318.         printf("Writing %d samples: ", cSamps);
  319.     for (iT = 0; iT < MAXPTSAMPS; iT++) /** Write samples **/
  320.         if (0 < rgmsDecided[iT].cMix) {
  321.             fseek(pfile, Samplepos, SEEK_SET);
  322.             ppsi = rgmsDecided[iT].ppsiMix;
  323.             pfileSamp = fopen((*ppsi)->fnSample, "rb");
  324.             if (NULL == pch)
  325.                 while ((ch = getc(pfileSamp)) != EOF)
  326.                     putc(ch, pfile);
  327.             else
  328.                 while (fwrite(pch, 1, fread(pch, 1, 512, pfileSamp), pfile) == 512);
  329.             cch = ftell(pfileSamp) / 2;
  330.             fclose(pfileSamp);
  331.             Samplepos = ftell(pfile);
  332.             fseek(pfile, 42 + 30 * iT, SEEK_SET);
  333.             putc(cch >> 8, pfile);
  334.             putc(cch & 0xFF, pfile);
  335.             if (!fQuiet)
  336.                 printf(".");
  337.         }
  338.     if (!fQuiet)
  339.         printf("\n");
  340.  
  341.     if (NULL != pch)
  342.         free(pch);
  343. }
  344.